﻿/* $Author: alexander.bsrgin@gmail.com $
 * $Id: LabyrinthModel.java 13 2011-11-15 19:32:47Z alexander.bsrgin@gmail.com $
 * 
 * Модель лабиринта. Класс реализовывает все вычисления, позволяет, запрещает
 * перемещать бегунок, возвращает бегунок в начало лабиринта. 
 */
package ru.habrahabr.prettyalarm;

import java.util.ArrayList;

/**
 * Модель лабиринта. Класс реализовывает все вычисления, позволяет, запрещает
 * перемещать бегунок, возвращает бегунок в начало лабиринта.
 * @author basargin
 */
public class LabyrinthModel
{
	public ArrayList<Point> route = null; // TODO предоставлять public-доступ к полю нехорошо, но мне лень
	public Point runner = null;
	private int stage;
	private OnLabyrinthEventHandler handler = null;
	int scalled_runner_bounds;
	
	/**
	 * Конструктор модели лабиринта в указанной области.
	 * @param l x координата левого края области
	 * @param t y координата верхнего края области
	 * @param r x координата правого края области
	 * @param b y координата нижнего края области
	 */
	LabyrinthModel(int l, int t, int r, int b, int scalled_runner_bounds)
	{
		this.scalled_runner_bounds = scalled_runner_bounds;
		generateRoute(l, t, r, b);
	}
	
	/**
	 * Инициализатор обработчика лабиринта.
	 * @param handler обработчик
	 */
	public void setOnLabyrinthEventHandler(OnLabyrinthEventHandler handler)
	{
		this.handler = handler;
	}
	
	/**
	 * Генератор маршрута лабиринта в указанной области.
	 * @param l x координата левого края области
	 * @param t y координата верхнего края области
	 * @param r x координата правого края области
	 * @param b y координата нижнего края области
	 */
	public void generateRoute(int l, int t, int r, int b)
	{
		// первая точка находится на maxY/2 по Y и +40 пикселей по X
		route = new ArrayList<Point>();
		route.addAll(new LabyrinthGenerator(l, t, r, b, 30).getRoute()); // TODO д.б. RUNNER_RADIUS
		runner = new Point(route.get(0));
		stage = 0;
	}
	
	/**
	 * Метод обработки уведомления о перемещении бегунка лабиринта.
	 * @param x новая координата бегунка по оси x
	 * @param y новая координата бегунка по оси y
	 */
	public void supplyRunnerPosition(int x, int y)
	{
		if (stage < 0) return;
		
		final Point from = route.get(stage);
		final Point to = route.get(stage+1);
		// рассчитываем допустимую границу перемещения пальца
		final Point bndlt = new Point(Math.min(from.x,to.x)-scalled_runner_bounds, Math.min(from.y,to.y)-scalled_runner_bounds);
		final Point bndrb = new Point(Math.max(from.x,to.x)+scalled_runner_bounds, Math.max(from.y,to.y)+scalled_runner_bounds);
		// если палец вышел за границу перемещения, то возвращаем бегунок
		// в начало этапа, обнуляем stage
		if (x < bndlt.x || x > bndrb.x || y < bndlt.y || y > bndrb.y)
		{
			if (stage == 0) return;
			stage = 0;
			runner.copyFrom(route.get(0));
			// уведомляем подписчика, например для включения вибратора
			if (handler != null)
				handler.onRollbackToBeginning();
			// Log.v("stage", String.valueOf(stage)); 
			return;
		}
		// проверяем переход на следующий этап
		// через узловую точку палец надо проводить аккуратнее и точнее,
		// нежели следовать по пути
		final int half = 2*scalled_runner_bounds / 3;
		final Point to_bndlt = new Point(to.x-half, to.y-half);
		final Point to_bndrb = new Point(to.x+half, to.y+half);
		if (x > to_bndlt.x && x < to_bndrb.x && y > to_bndlt.y && y < to_bndrb.y)
		{
			stage++;
			// уведомляем подписчика, например для включения вибратора
			if (handler != null)
				handler.onPassStage();
			// Log.v("stage", String.valueOf(stage));
			// если достигли конца маршрута, то прекращаем активность в этом окне,
			// создаём Intent для завершения текущего Activity
			if ((stage+1) == route.size())
			{
				stage = -1;
				runner.copyFrom(route.get(route.size()-1));
				if (handler != null)
					handler.onFinished();
			}
			return;
		}
		
		// если бегунок перемещается по вертикальной линии, то меняется только
		// координата по оси y, в противном случае по x
		if (from.x == to.x)
		{
			runner.x = from.x;
			runner.y = getStagedCoordinate(from.y, to.y, y);
		}
		else
		{
			runner.x = getStagedCoordinate(from.x, to.x, x);
			runner.y = from.y;
		}
	}
	
	/**
	 * Сброс пройдённого бегунком пути. Перемещение бегунка в начало.
	 */
	public void resetRunner()
	{
		runner.copyFrom(route.get(0));
	}
	
	private int getStagedCoordinate(int from, int to, int current)
	{
		if (from < to)
		{
			if (current < from)
				return from;
			else if (current > to)
				return to;
		}
		else
		{
			if (current > from)
				return from;
			else if (current < to)
				return to;
		}
		return current;
	}
	
	/**
	 * Класс с описанием "точки". Хранит координаты по осям x и y.
	 */
	public static class Point
	{
		int x;
		int y;
		Point(Point p)
		{
			x = p.x;
			y = p.y;
		}
		Point(int x, int y)
		{
			this.x = x;
			this.y = y;
		}
		void copyFrom(final Point src)
		{
			x = src.x;
			y = src.y;
		}
	}
}
